#include "spi.h"
#include "ptdebug.h"
#include "string.h"
extern int32_t pt_spi_bus_device_init(struct pt_spi_bus *bus, uint8_t device_id);
extern int32_t pt_spi_dev_device_init(struct pt_spi_device *dev, uint8_t device_id);

int32_t pt_spi_bus_register(struct pt_spi_bus       *bus,
                             uint8_t 				device_id,
                             const struct pt_spi_ops *ops)
{
    int32_t result;

    result = pt_spi_bus_device_init(bus, device_id);
    if (result != PT_EOK)
        return result;

    /* initialize mutex lock */
//    pt_mutex_init(&(bus->lock), name, PT_IPC_FLAG_FIFO);
    /* set ops */
    bus->ops = ops;
    /* initialize owner */
    bus->owner = PT_NULL;
    /* set bus mode */
    bus->mode = PT_SPI_BUS_MODE_SPI;

    return PT_EOK;
}

int32_t pt_spi_bus_attach_device(struct pt_spi_device *device,
                                  uint8_t 				dev_id,
                                  uint8_t           	bus_id,
                                  void                 *user_data)
{
    int32_t result;
    pt_device_t bus;

    /* get physical spi bus */
    bus = pt_device_find(bus_id);
    if (bus != PT_NULL && bus->type == PT_Device_Class_SPIBUS){
		
        device->bus = (struct pt_spi_bus *)bus;

        /* initialize spidev device */
        result = pt_spi_dev_device_init(device, dev_id);
        if (result != PT_EOK)
            return result;

        memset(&device->config, 0, sizeof(device->config));
        device->parent.user_data = user_data;

        return PT_EOK;
    }

    /* not found the host bus */
    return -PT_ERROR;
}

int32_t pt_spi_configure(struct pt_spi_device        *device,
                          struct pt_spi_configuration *cfg)
{
    int32_t result;

    PT_ASSERT(device != PT_NULL);

    /* set configuration */
    device->config.data_width = cfg->data_width;
    device->config.mode       = cfg->mode & PT_SPI_MODE_MASK ;
    device->config.max_hz     = cfg->max_hz ;

    if (device->bus != PT_NULL){
        result = pt_mutex_take(&(device->bus->lock));
        if (result == PT_EOK){
            if (device->bus->owner == device){
                device->bus->ops->configure(device, &device->config);
            }

            /* release lock */
            pt_mutex_release(&(device->bus->lock));
        }else{
			return PT_ERROR;
		}
    }

    return PT_EOK;
}

int32_t pt_spi_send_then_send ( struct pt_spi_device * device,
								const void      *     send_buf1,
								uint32_t             send_length1,
								const void      *     send_buf2,
								uint32_t             send_length2 )
{
	int32_t result;
	struct pt_spi_message message;

	PT_ASSERT ( device != PT_NULL );
	PT_ASSERT ( device->bus != PT_NULL );

	result = pt_mutex_take ( & ( device->bus->lock ) );

	if ( result == PT_EOK ) {
		if ( device->bus->owner != device ) {
			/* not the same owner as current, re-configure SPI bus */
			result = device->bus->ops->configure ( device, &device->config );

			if ( result == PT_EOK ) {
				/* set SPI bus owner */
				device->bus->owner = device;

			} else {
				/* configure SPI bus failed */
				result = -PT_EIO;
				goto __exit;
			}
		}

		/* send data1 */
		message.send_buf   = send_buf1;
		message.recv_buf   = PT_NULL;
		message.length     = send_length1;
		message.cs_take    = 1;
		message.cs_release = 0;
		message.next       = PT_NULL;

		result = device->bus->ops->xfer ( device, &message );

		if ( result == 0 ) {
			result = -PT_EIO;
			goto __exit;
		}

		/* send data2 */
		message.send_buf   = send_buf2;
		message.recv_buf   = PT_NULL;
		message.length     = send_length2;
		message.cs_take    = 0;
		message.cs_release = 1;
		message.next       = PT_NULL;

		result = device->bus->ops->xfer ( device, &message );

		if ( result == 0 ) {
			result = -PT_EIO;
			goto __exit;
		}

		result = PT_EOK;

	} else {
		return -PT_EIO;
	}

__exit:
	pt_mutex_release ( & ( device->bus->lock ) );

	return result;
}

int32_t pt_spi_send_then_recv ( struct pt_spi_device * device,
								const void      *     send_buf,
								uint32_t             send_length,
								void         *        recv_buf,
								uint32_t             recv_length )
{
	int32_t result;
	struct pt_spi_message message;

	PT_ASSERT ( device != PT_NULL );
	PT_ASSERT ( device->bus != PT_NULL );

	result = pt_mutex_take ( & ( device->bus->lock ) );

	if ( result == PT_EOK ) {
		if ( device->bus->owner != device ) {
			/* not the same owner as current, re-configure SPI bus */
			result = device->bus->ops->configure ( device, &device->config );

			if ( result == PT_EOK ) {
				/* set SPI bus owner */
				device->bus->owner = device;

			} else {
				/* configure SPI bus failed */
				result = -PT_EIO;
				goto __exit;
			}
		}

		/* send data */
		message.send_buf   = send_buf;
		message.recv_buf   = PT_NULL;
		message.length     = send_length;
		message.cs_take    = 1;
		message.cs_release = 0;
		message.next       = PT_NULL;

		result = device->bus->ops->xfer ( device, &message );

		if ( result == 0 ) {
			result = -PT_EIO;
			goto __exit;
		}

		/* recv data */
		message.send_buf   = PT_NULL;
		message.recv_buf   = recv_buf;
		message.length     = recv_length;
		message.cs_take    = 0;
		message.cs_release = 1;
		message.next       = PT_NULL;

		result = device->bus->ops->xfer ( device, &message );

		if ( result == 0 ) {
			result = -PT_EIO;
			goto __exit;
		}

		result = PT_EOK;

	} else {
		return -PT_EIO;
	}

__exit:
	pt_mutex_release ( & ( device->bus->lock ) );

	return result;
}

uint32_t pt_spi_transfer ( struct pt_spi_device * device,
						   const void      *     send_buf,
						   void         *        recv_buf,
						   uint32_t             length )
{
	int32_t result;
	struct pt_spi_message message;

	PT_ASSERT ( device != PT_NULL );
	PT_ASSERT ( device->bus != PT_NULL );

	result = pt_mutex_take ( & ( device->bus->lock ) );

	if ( result == PT_EOK ) {
		if ( device->bus->owner != device ) {
			/* not the same owner as current, re-configure SPI bus */
			result = device->bus->ops->configure ( device, &device->config );

			if ( result == PT_EOK ) {
				/* set SPI bus owner */
				device->bus->owner = device;

			} else {
				/* configure SPI bus failed */
//				rt_set_errno ( -PT_EIO );
				result = PT_EIO;
				goto __exit;
			}
		}

		/* initial message */
		message.send_buf   = send_buf;
		message.recv_buf   = recv_buf;
		message.length     = length;
		message.cs_take    = 1;
		message.cs_release = 1;
		message.next       = PT_NULL;

		/* transfer message */
		result = device->bus->ops->xfer ( device, &message );

		if ( result == 0 ) {
//			rt_set_errno ( -PT_EIO );
			goto __exit;
		}

	} else {
//		rt_set_errno ( -PT_EIO );

		return 0;
	}

__exit:
	pt_mutex_release ( & ( device->bus->lock ) );

	return result;
}

struct pt_spi_message *pt_spi_transfer_message(struct pt_spi_device  *device,
                                               struct pt_spi_message *message)
{
    int32_t result;
    struct pt_spi_message *index;

    PT_ASSERT(device != PT_NULL);

    /* get first message */
    index = message;
    if (index == PT_NULL)
        return index;

    result = pt_mutex_take(&(device->bus->lock));
    if (result != PT_EOK)
    {
//        rt_set_errno(-RT_EBUSY);

        return index;
    }

    /* reset errno */
//    rt_set_errno(PT_EOK);

    /* configure SPI bus */
    if (device->bus->owner != device)
    {
        /* not the same owner as current, re-configure SPI bus */
        result = device->bus->ops->configure(device, &device->config);
        if (result == PT_EOK)
        {
            /* set SPI bus owner */
            device->bus->owner = device;
        }
        else
        {
            /* configure SPI bus failed */
//            rt_set_errno(-PT_EIO);
            goto __exit;
        }
    }

    /* transmit each SPI message */
    while (index != PT_NULL)
    {
        /* transmit SPI message */
        result = device->bus->ops->xfer(device, index);
        if (result == 0)
        {
//            rt_set_errno(-PT_EIO);
            break;
        }

        index = index->next;
    }

__exit:
    /* release bus lock */
    pt_mutex_release(&(device->bus->lock));

    return index;
}

int32_t pt_spi_take_bus(struct pt_spi_device *device)
{
    int32_t result = PT_EOK;

    PT_ASSERT(device != PT_NULL);
    PT_ASSERT(device->bus != PT_NULL);

    result = pt_mutex_take(&(device->bus->lock));
    if (result != PT_EOK)
    {
//        rt_set_errno(-RT_EBUSY);

        return -PT_EBUSY;
    }

    /* reset errno */
//    rt_set_errno(PT_EOK);

    /* configure SPI bus */
    if (device->bus->owner != device)
    {
        /* not the same owner as current, re-configure SPI bus */
        result = device->bus->ops->configure(device, &device->config);
        if (result == PT_EOK)
        {
            /* set SPI bus owner */
            device->bus->owner = device;
        }
        else
        {
            /* configure SPI bus failed */
//            rt_set_errno(-PT_EIO);
            /* release lock */
            pt_mutex_release(&(device->bus->lock));

            return -PT_EIO;
        }
    }

    return result;
}

int32_t pt_spi_release_bus(struct pt_spi_device *device)
{
    PT_ASSERT(device != PT_NULL);
    PT_ASSERT(device->bus != PT_NULL);
    PT_ASSERT(device->bus->owner == device);

    /* release lock */
    pt_mutex_release(&(device->bus->lock));

    return PT_EOK;
}

int32_t pt_spi_take(struct pt_spi_device *device)
{
    int32_t result;
    struct pt_spi_message message;

    PT_ASSERT(device != PT_NULL);
    PT_ASSERT(device->bus != PT_NULL);

    memset(&message, 0, sizeof(message));
    message.cs_take = 1;

    result = device->bus->ops->xfer(device, &message);

    return result;
}

int32_t pt_spi_release(struct pt_spi_device *device)
{
    int32_t result;
    struct pt_spi_message message;

    PT_ASSERT(device != PT_NULL);
    PT_ASSERT(device->bus != PT_NULL);

    memset(&message, 0, sizeof(message));
    message.cs_release = 1;

    result = device->bus->ops->xfer(device, &message);

    return result;
}
